/**
	License:		Artistic license 2.0 (see license.txt, also available online at http://www.perlfoundation.org/artistic_license_2_0)
	Authors:		OrbitalLab (http://www.orbitallab.ru/moonglide/), 2007-2009

	File:			OpenGLShaderARB.d
	Description:	opengl shader interface for ARB programs
	Date:			22.12.2007 by Jurec
*/

module moonglide.renderer.opengl.OpenGLShaderARB;

import
	moonglide.util.MoonUtil,
	moonglide.renderer.IRendererRes,

	moonglide.renderer.opengl.GL,
	moonglide.renderer.opengl.OpenGLRenderer,
	moonglide.renderer.opengl.OpenGLShaderWork,

	moonglide.resources.resobjects.hilevel.ResShaderComposite;


class COpenGLShaderARBProvider : IShaderProvider
{
	protected:
		uint _GlConst(in ubyte ShT)
		{
			return (ShT == sh_Vertex) ? GL_VERTEX_PROGRAM_ARB : GL_FRAGMENT_PROGRAM_ARB;
		}

		bool UploadProg(inout CResShader sh)
		{
			if( !sh )
				return false;

			sh.setLoading;

			uint shaderType = _GlConst( sh.sType );
			glEnable( shaderType );
			glGenProgramsARB( 1, &sh.handle );
			glBindProgramARB( shaderType, sh.handle);


			char[]
				text = "",
				newline = "\n";

			foreach( str; sh.text )
				text ~= str ~ newline;

			glProgramStringARB( shaderType, GL_PROGRAM_FORMAT_ASCII_ARB,
							   text.length, text.ptr);
			glDisable( shaderType );
			char[] err = CCharToDChar( glGetString(GL_PROGRAM_ERROR_STRING_ARB) );

			if( err.length > 1 )
			{
		//		GetGLError(__FILE__,__LINE__);
				int i;
				glGetIntegerv(GL_PROGRAM_ERROR_POSITION_ARB, &i);

				errorLog( "Shader program " ~ sh.id() ~ " contains errors: "  );
				moonAssert( 0, err, __FILE__, __LINE__ );
				return false;
			}

			sh.setLoaded;
			return true;
		}

		bool AnalyzeProg( inout CResShader sh )
		{

			void zAddLocal( char[] Xname, int Xnum )
			{
				sh.locals.length = sh.locals.length + 1;
				with( sh.locals[$-1] )
				{
					name = Xname;
					num = Xnum;
				}
			}
			char[] name, arr, number;
			int j,num = 0;

	//		if (sh.text.length > 0)
	//			sh.sType = containsPattern( sh.text[0] , "!!ARBvp" ) ? sh_Vertex : sh_Fragment;

			foreach( str; sh.text )
			{
				if( !str.length )
					continue;

				if( containsPattern( str, "PARAM" ) && containsPattern( str, "program.local" ) )
				{
					number = str[ locate(str, '[')+1 .. locate(str, ']') ];
					name = tango.text.Util.trim( str[ locatePattern(str, "PARAM")+6 .. locate(str, '=') ] );

					if( containsPattern( str, ".." ) )
					{
						arr =  str[ locatePrior(str, '[')+1 .. locatePatternPrior(str, "..") ];
						num = strToInt( arr );

						name = name[ 0 .. locate(name, '[')+1 ] ;
						for( j = 0; j < strToInt(number); ++j )// array of locals.
						{
							zAddLocal( name ~ intToStr(j) ~ "]", num + j );
						}
					} else
					{
						zAddLocal( name ,  strToInt( number ) );
					}

				}
			}

			debug
			{
				debugLog.trace(" -----" ~ sh.id() ~ "----- ");

				foreach( loc; sh.locals )
					debugLog.trace( Format( "{} {} {}", loc.name, loc.num, loc.isStatic ));

				debugLog.trace(" ---------- ");
			}

			return true;
		}

	public:
		bool Upload(inout CResShaderComposite sh)
		{
			bool res = true;
			foreach( inout Pr; sh.progs )
				if( Pr )
				if( !UploadProg( Pr ) )
				{
					Pr = null;
					res = false;
				}
			return res;
		}

		bool Unload(inout CResShaderComposite sh)
		{
			foreach( inout Pr; sh.progs )
				if( Pr )
				{
					glDeleteProgramsARB( 1, &Pr.handle );
					Pr.setNotLoaded;
				}

			return true;
		}

		void Bind(inout CResShaderComposite sh)
		{
			uint co;
			foreach( Pr; sh.progs )
				if( Pr )
				{
					co = _GlConst( Pr.sType );
					glEnable( co );
					glBindProgramARB( co, Pr.handle );
				}
		}

		bool Link(inout CResShaderComposite sh)
		{
			return true;
		}

		void Unbind(inout CResShaderComposite sh)
		{
			foreach( Pr; sh.progs )
				if( Pr )
				{
					glDisable( _GlConst( Pr.sType ) );
				}
		}

		bool Analyze(inout CResShaderComposite sh)
		{
			bool res = true;
			foreach( inout Pr; sh.progs )
				if( Pr )
					if( !AnalyzeProg( Pr ) )
						res = false;
			return res;
		}


		void Enable()
		{
			glEnable(GL_VERTEX_PROGRAM_ARB);
			glEnable(GL_FRAGMENT_PROGRAM_ARB);
		}


		void Disable()
		{
			glDisable(GL_VERTEX_PROGRAM_ARB);
			glDisable(GL_FRAGMENT_PROGRAM_ARB);
		}


		void SetLocal( inout SShaderLoc lo, ubyte sTy )
		{
			uint St = _GlConst(sTy);
			if( lo.floatUse )
			{
				Vector4f da = lo.getFData();
				glProgramLocalParameter4fARB( St , lo.num , da.x, da.y, da.z, da.w );
			} else
			{
				moonAssert( 0, "integer locals isn`t used in ARB shaders", __FILE__, __LINE__ );
			}
			lo.binded = true;
		}


		void SetEnv( inout SShaderLoc shLoc, ubyte sType )
		{
			uint shaderType = _GlConst( sType );

			if( shLoc.floatUse )
			{
				Vector4f da = shLoc.getFData();
				glProgramEnvParameter4fARB( shaderType, shLoc.num , da.x, da.y, da.z, da.w );
			} else
			{
				moonAssert( 0, "integer locals isn`t used in ARB shaders", __FILE__, __LINE__ );
			}

			shLoc.binded = true;
		}
}
